/*! ========================================================================
** Extended Template Library
** Box Blur Template Implementation
**
** Copyright (c) 2002 Robert B. Quattlebaum Jr.
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
** published by the Free Software Foundation; either version 2 of
** the License, or (at your option) any later version.
**
** This package is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** === N O T E S ===========================================================
**
** This is an internal header file, included by other ETL headers.
** You should not attempt to use it directly.
**
** ========================================================================= */

#ifndef __ETL__BOXBLUR_H
#define __ETL__BOXBLUR_H

#include <algorithm>

_ETL_BEGIN_NAMESPACE

template<typename T1, typename T2> void
hbox_blur(T1 pen, int w, int h, int length, T2 outpen)
{
    int x, y;
    typename T1::iterator_x iter, end;

    length = std::min(w, length);
    const float divisor(1.0f / (length * 2 + 1));

    for (y = 0; y < h; y++, pen.inc_y(), outpen.inc_y()) {
        iter = pen.x();
        end = pen.end_x();

        typename T1::accumulator_type tot((typename T1::accumulator_type)(*iter) * (length + 1));

        for (x = 0; x < length && iter != end; x++, ++iter) {
            tot += *iter;
        }

        iter = pen.x();

        for (x = 0; x < w && iter != end; x++, ++iter, outpen.inc_x()) {
            tot -= (x > length) ?
                   (typename T1::accumulator_type)(iter[-length - 1]) :
                   (typename T1::accumulator_type)(*pen.x());
            tot += ((x + length) < w) ?
                   (typename T1::accumulator_type)(iter[length]) :
                   (typename T1::accumulator_type)(end[-1]);

            outpen.put_value((typename T2::value_type)(tot * divisor));
        }

        outpen.dec_x(x);
    }
}

template<typename T1, typename T2> void
vbox_blur(T1 pen, const int w, const int h, int length, T2 outpen)
{
    int x, y;
    typename T1::iterator_y iter, end;

    length = std::min(h, length);
    const float divisor(1.0f / (length * 2 + 1));

    for (x = 0; x < w; x++, pen.inc_x(), outpen.inc_x()) {
        iter = pen.y();
        end = pen.end_y();

        typename T1::accumulator_type tot((typename T1::accumulator_type)(*iter) * (length + 1));

        for (y = 0; y < length && iter != end; y++, ++iter) {
            tot += *iter;
        }

        iter = pen.y();

        for (y = 0; y < h && iter != end; y++, ++iter, outpen.inc_y()) {
            tot -= (y > length) ?
                   (typename T1::accumulator_type)(iter[-length - 1]) :
                   (typename T1::accumulator_type)(*pen.y());
            tot += ((y + length) < h) ?
                   (typename T1::accumulator_type)(iter[length]) :
                   (typename T1::accumulator_type)(end[-1]);

            outpen.put_value((typename T2::value_type)(tot * divisor));
        }

        outpen.dec_y(y);
    }
}

template<typename T1, typename T2> void
box_blur(T1 pen, int w, int h, int blur_w, int blur_h, T2 outpen)
{
    hbox_blur(pen, w, h, blur_w, outpen);
    vbox_blur(pen, w, h, blur_h, outpen);
}

template<typename T1, typename T2> void
box_blur(T1 pen, int w, int h, int size, T2 outpen)
{
    hbox_blur(pen, w, h, size, outpen);
    vbox_blur(pen, w, h, size, outpen);
}

template<typename T1, typename T2> void
hbox_blur(T1 begin, T1 end, int len, T2 outpen)
{
    typename T1::difference_type size(end - begin);
    hbox_blur(begin, size.x, size.y, len, outpen);
}

template<typename T1, typename T2> void
vbox_blur(T1 begin, T1 end, int len, T2 outpen)
{
    typename T1::difference_type size(end - begin);
    vbox_blur(begin, size.x, size.y, len, outpen);
}

template<typename T1, typename T2> void
box_blur(T1 begin, T1 end, int blur_w, int blur_h, T2 outpen)
{
    typename T1::difference_type size(end - begin);
    hbox_blur(begin, size.x, size.y, blur_w, outpen);
    vbox_blur(begin, size.x, size.y, blur_h, outpen);
}

template<typename T1, typename T2> void
box_blur(T1 begin, T1 end, int blursize, T2 outpen)
{
    typename T1::difference_type size(end - begin);
    hbox_blur(begin, size.x, size.y, blursize, outpen);
    vbox_blur(begin, size.x, size.y, blursize, outpen);
}

_ETL_END_NAMESPACE

#endif